/*
 * @Author: wwssaabb
 * @Date: 2021-09-06 15:46:14
 * @LastEditTime: 2021-09-16 09:51:19
 * @FilePath: \handwritten-code\demo\继承.js
 */

/* 
  原型链继承
  存在的问题：
    1.原型中包含的引用类型属性被所有实例共享
    2.子类实例化时不能给父类传参
*/

function Animal() {
  this.colors = ['black', 'white']
}

Animal.prototype.getColor = function () {
  return this.colors
}

function Dog() {}
Dog.prototype = new Animal() //狗类创建实例的原型是Animal的实例{colors : ['black', 'white']}
Dog.prototype.constructor = Dog

let dog1 = new Dog() //新建一只狗
dog1.getColor() //这只狗可以调用getColor方法，拿到的是Animal实例的colors

let dog2 = new Dog() //新建另一只狗
dog2.colors.push('yellow') //这只狗修改了Animal实例的colors
console.log(dog2.getColor()) //得到的是修改后的colors [ 'black', 'white', 'yellow' ]

/* 
  借用构造函数
  优点：
    1.各自拥有自己的实例
    2.子类可以向父类传参
  缺点：
    1.无法复用父类的公共函数
    2.每次实例创建时都执行一次父类函数
*/
function Parent(name) {
  this.name = name
}

function Child(name) {
  Parent.call(this, name) //借用Parent的构造函数,可以达到向Parent传参，对Child实例初始化
}

let child1 = new Child('张三')

let child2 = new Child('李四')

console.log(child1)
console.log(child2)


/* 
  组合式继承（原型链继承和借用构造函数合并）
  优点：
    1.解决实例属性共享问题
    2.子类可以向父类传参
    3.实现父类方法的复用
  缺点：
    1.需要执行两次父类构造函数
*/

function Parent(name) {
  this.name = name
  this.body = ['foot', 'hand']
}

function Child(name, age) {
  Parent.call(this, name) //借用Parent构造函数，初始化Child实例对象
  this.age = age //
}

Child.prototype = new Parent()
Child.prototype.constructor = Child

const child1 = new Child('张三', 23)
child1.body.push('head')
const child2 = new Child('李四', 23)
child2.body.push('eye')

console.log(child1.body)
console.log(child2.body)



/* 
  原型式继承
  缺点：同原型链继承一样，每个实例对引用类型属性的修都会被其它实例共享
*/

function createObj(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
var person = { //创建原型person
  name: 'arzh',
  body: ['foot', 'hand']
}
var person1 = createObj(person)
var person2 = createObj(person)

console.log(person1) //arzh
person1.body.push('head')
console.log(person2) //[ 'foot', 'hand', 'head' ]

/* 
  寄生式继承
  缺点：同借用构造函数一样，无法复用父类函数，每次创建对象都会创建一遍方法
*/

function createEnhanceObj(o) {
  let clone = Object.create(o)
  clone.getName = function () {
    console.log('张三')
  }
  return clone
}

/* 
  寄生组合式继承
  优点：不需要为了指定子类的原型而调用父类的构造函数
*/

function inheritPrototype(Parent, Child) {
  Child.prototype = Object.create(Parent.prototype) //创建父类原型的一个副本,把副本赋值给子类原型
  Child.prototype.constructor = Child;
}

function Parent(name) { //父类构造函数
  this.name = name
}

Parent.prototype.getName = function () { //父类原型上设置getName方法
  console.log(this.name)
}
Parent.prototype.getColor = function () { //父类原型上设置getName方法
  console.log(this.color)
}

function Child(name, color) { //子类构造函数
  Parent.call(this, name) //调用父类构造函数初始化实例
  this.color = color //初始化自己的属性
}

inheritPrototype(Parent, Child)

let Child1 = new Child('张三', 'red')
console.log(Child1.name) // '张三'
console.log(Child1.color) // 'red'


/* 
  ES6 Class继承
*/

class Point {
  constructor(x, y) {
    this.x = x
    this.y = y
  }

  toString() {
    return this.x + '' + this.y
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y) //调用父类的constructor(x, y)
    this.color = color
  }

  toString() {
    return this.color + ' ' + super.toString() // 调用父类的toString()
  }
}

var colorPoint = new ColorPoint('1', '2', 'red')

console.log(colorPoint.toString()) // red 12